package com.ksc.storekeeper.shared.utils.conversion;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.ksc.storekeeper.server.entities.Product;
import com.ksc.storekeeper.shared.dto.ProductDTO;

public class ConversionUtils {

	public static final String DTO_PACKAGE = "com.ksc.storekeeper.shared.dto";
	public static final String ENTITY_PACKAGE = "com.ksc.storekeeper.server.entities";

	@SuppressWarnings({ "rawtypes", "unused" })
	private static Class getClass(Class classOne,String methodName){
		for(Method method:classOne.getMethods()){
			if(method.getName().equals(methodName)) {
				return classOne;
			}
		}
		return getClass(classOne.getSuperclass(),methodName);
	}
	
	private static void setId(Object from, Object to) throws Exception {
		Method getIdMethodFrom = from.getClass().getMethod("getId");
		Method getIdMethodTo = to.getClass().getMethod("getId");		
		Method setIdMethodTo = to.getClass().getMethod("setId",getIdMethodTo.getReturnType());
		Object fromValue = getIdMethodFrom.invoke(from);
		if(fromValue != null){
			setIdMethodTo.invoke(to, fromValue);
		}
	}

	public static void convert(Product from,ProductDTO to){
		to.setCode(from.getCode());
		to.setCreated(from.getCreated());
		to.setId(from.getId());
		to.setLastModified(from.getLastModified());
		to.setVersion(from.getVersion());
		to.setName(from.getName());
		to.setDescription(from.getDescription());
		to.setImageUrl(from.getImageUrl());
		to.setCategory(from.getCategory());
	}
	
	public static void convert(ProductDTO from,Product to){
		to.setCode(from.getCode());
		to.setCreated(from.getCreated());
		to.setLastModified(from.getLastModified());
		to.setName(from.getName());
		to.setVersion(from.getVersion());
		to.setId(from.getId());
		to.setDescription(from.getDescription());
		to.setImageUrl(from.getImageUrl());
		to.setCategory(from.getCategory());
	}
	
	
	@SuppressWarnings("rawtypes")
	public static Object convert(Object from, Object to) {
		try {
			if (from == null) {
				to = null;
				return to;
			}
			if(!from.getClass().getSimpleName().contains("DTO"))
				setId(from,to);

			Field fields[] = from.getClass().getDeclaredFields();
			Field moreFields[] = from.getClass().getSuperclass().getDeclaredFields();
			List<Field> allFields = new ArrayList<Field>();
			Collections.addAll(allFields, fields);
			Collections.addAll(allFields, moreFields);

			for (Field field : allFields) {

				if (field.getName().equals("serialVersionUID"))
					continue;

				Method getMethodFrom = from.getClass().getMethod(getMethodNameFromField(field.getName(), "get"));

				// If it is not primitive or String or Object type then it is
				// not simply converted
				// we have to do deep conversion
				if (!field.getType().isPrimitive() && !field.getType().equals(String.class) && !field.getType().equals(Object.class)) {

					// For List
					if (field.getType().equals(List.class)) {

						for (Object val : (List) getMethodFrom.invoke(from)) {

							// List of primitive and String is easily converted
							if (val.getClass().isPrimitive() || val.getClass().equals(String.class)) {
								Method setMethodTo = to.getClass().getMethod(getMethodNameFromField(field.getName(), "set"), getMethodFrom.getReturnType());
								setMethodTo.invoke(to, getMethodFrom.invoke(from));
								break;
							} else {// List of complex object need to be done
									// with deep conversion
								String newClass = val.getClass().getSimpleName();
								String packageName = "";
								if (newClass.contains("DTO")) {
									newClass = newClass.replace("DTO", "");
									packageName = ENTITY_PACKAGE;
								} else {
									newClass = newClass.concat("DTO");
									packageName = DTO_PACKAGE;
								}
								Class instClass = Class.forName(packageName + "." + newClass);
								Method addMethodTo = to.getClass().getMethod(getMethodNameFromField(field.getName(), "addTo"), instClass);
								Object obj = instClass.newInstance();
								convert(val, obj);
								addMethodTo.invoke(to, obj);
							}
						}
					} else {// Complex object needs to do deep conversion
						Method getMethodTo = to.getClass().getMethod(getMethodNameFromField(field.getName(), "get"));
						convert(getMethodFrom.invoke(from), getMethodTo.invoke(to));
					}
				} else {// Primitive/String/Object type conversion
					Method setMethodTo = to.getClass().getMethod(getMethodNameFromField(field.getName(), "set"), getMethodFrom.getReturnType());
					setMethodTo.invoke(to, getMethodFrom.invoke(from));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}
		return to;
	}

	private static String getMethodNameFromField(String field, String type) {
		StringBuilder stringBuilder = new StringBuilder(field);
		stringBuilder.replace(0, 1, stringBuilder.substring(0, 1).toUpperCase());
		return type + stringBuilder.toString();
	}

}
